﻿#include "LineChartStraightLine.h"
#include "BaseWidget/IWidgetSignal.h"

namespace pst
{
    LineChartStraightLine::LineChartStraightLine(QCustomPlot* parentPlot, Type type) : QCPItemStraightLine(parentPlot)
    {
        m_type = type;
        this->setLayer("overlay");
        this->setPen(QPen(Qt::black, 1, Qt::DotLine));
        m_axisLabel.label = new QCPItemText(parentPlot);
        m_axisLabel.label->setLayer("overlay");
        m_axisLabel.label->setClipToAxisRect(false);
        m_axisLabel.label->setPen(QPen(Qt::black));
        m_axisLabel.label->setBrush(Qt::white);
        m_axisLabel.label->setSelectedPen(QPen(Qt::blue));
        m_axisLabel.label->setSelectedBrush(Qt::white);
        m_axisLabel.label->setPadding({5, 5, 5, 5});
        m_axisLabel.arrow = new QCPItemLine(parentPlot);
        m_axisLabel.arrow->setLayer("overlay");
        m_axisLabel.arrow->setClipToAxisRect(false);
        m_axisLabel.arrow->setHead(QCPLineEnding::esLineArrow);
        if (m_type == Type::XAxis)
        {
            m_axisLabel.label->position->setTypeY(QCPItemPosition::ptAxisRectRatio);
            m_axisLabel.arrow->start->setParentAnchor(m_axisLabel.label->top);
            m_axisLabel.arrow->end->setTypeY(QCPItemPosition::ptAxisRectRatio);
        }
        else if (m_type == Type::YAxis)
        {
            m_axisLabel.label->setRotation(-90);
            m_axisLabel.label->position->setTypeX(QCPItemPosition::ptAxisRectRatio);
            m_axisLabel.arrow->start->setParentAnchor(m_axisLabel.label->bottom);
            m_axisLabel.arrow->end->setTypeX(QCPItemPosition::ptAxisRectRatio);
        }
        m_guideLineProperty = new Style2D_GuideLineProperty();
        connect(this, &LineChartTracer::selectionChanged, this, [this](bool selected)
            { 
                slot_selectChange(selected);
                QString _pyCode = QString("PostProcessing.lineChartLineChartStraightLineSelectChange(%1,%2)").arg(getLineChartStraightLineID()).arg(selected);
                emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
            });
        connect(m_axisLabel.label, &QCPItemText::selectionChanged, this, [this](bool selected)
                { 
                slot_selectChange(selected);
                QString _pyCode = QString("PostProcessing.lineChartLineChartStraightLineSelectChange(%1,%2)").arg(getLineChartStraightLineID()).arg(selected);
                emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
            });
        connect(m_axisLabel.arrow, &QCPItemLine::selectionChanged, this, [this](bool selected)
                {
                slot_selectChange(selected);
                QString _pyCode = QString("PostProcessing.lineChartLineChartStraightLineSelectChange(%1,%2)").arg(getLineChartStraightLineID()).arg(selected);
                emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
            });

        connect(IWidgetSignalInstance, &BaseWidget::IWidgetSignal::signal_lineChartAxisLineUpdate, this, [this, parentPlot](QString typeAxis, int id, double value)
         {
            if(!parentPlot->isVisible()) return;
                
            if (id != getLineChartStraightLineID()) {
                return;
            }

            if (typeAxis=='x') {
                XAxisLineUpdate(value);
            }
            else if (typeAxis == 'y')
            {
                YAxisLineUpdate(value);
            } 
            parentPlot->replot();
        });

        connect(IWidgetSignalInstance, &BaseWidget::IWidgetSignal::signal_lineChartLineChartStraightLineSelectChange, this, [this, parentPlot](int id, bool selected)
                {
            if(!parentPlot->isVisible()) return;
            if (id != getLineChartStraightLineID())
                return;
            slot_selectChange(selected);
            parentPlot->replot();
        });

    }

    LineChartStraightLine::~LineChartStraightLine()
    {
        auto parentPlot = qobject_cast<QCustomPlot*>(this->parent());
        if (parentPlot == nullptr)
        {
            return;
        }
        for (auto& dataLabel : m_dataLabelList)
        {
            if (parentPlot->hasItem(dataLabel.second.label))
            {
                parentPlot->removeItem(dataLabel.second.label);
            }
            if (parentPlot->hasItem(dataLabel.second.arrow))
            {
                parentPlot->removeItem(dataLabel.second.arrow);
            }
        }
        if (parentPlot->hasItem(m_axisLabel.label))
        {
            parentPlot->removeItem(m_axisLabel.label);
        }
        if (parentPlot->hasItem(m_axisLabel.arrow))
        {
            parentPlot->removeItem(m_axisLabel.arrow);
        }
    }

    void LineChartStraightLine::XAxisLineUpdate(double x_value)
    {
        this->point1->setCoords(x_value, 0);
        this->point2->setCoords(x_value, 1);
    }
    void LineChartStraightLine::updataStyle2DGuideLineProperty()
    {
        m_guideLineProperty->m_value = this->GetValue();
        m_guideLineProperty->m_guideLineStyle = this->pen().style() - 1;
        m_guideLineProperty->m_guideLineThickness = this->pen().widthF();
        m_guideLineProperty->m_guideLineColor = this->pen().color();
    }

    void LineChartStraightLine::YAxisLineUpdate(double y_value)
    {
        this->point1->setCoords(0, y_value);
        this->point2->setCoords(1, y_value);
    }

    void LineChartStraightLine::slot_selectChange(bool selected)
    {
        this->blockSignals(true);
        m_axisLabel.label->blockSignals(true);
        m_axisLabel.arrow->blockSignals(true);
        this->setSelected(selected);
        m_axisLabel.label->setSelected(selected);
        m_axisLabel.arrow->setSelected(selected);
        this->blockSignals(false);
        m_axisLabel.label->blockSignals(false);
        m_axisLabel.arrow->blockSignals(false);

    }

    void LineChartStraightLine::slot_update()
    {
        if (m_type == Type::XAxis)
        {
            XLineLabelUpdate(this->point1->coords().x());
        }
        else if (m_type == Type::YAxis)
        {
            YLineLabelUpdate(this->point1->coords().y());
        }
    }

    void LineChartStraightLine::mousePressEvent(QMouseEvent* event, const QVariant& details)
    {
        Q_UNUSED(details)
        event->accept();
    }

    void LineChartStraightLine::mouseMoveEvent(QMouseEvent* event, const QPointF& startPos)
    {
        auto parentPlot = qobject_cast<QCustomPlot*>(this->parent());
        if (parentPlot == nullptr)
        {
            return;
        }
        double x_value = parentPlot->xAxis->pixelToCoord(event->pos().x());
        double y_value = parentPlot->yAxis->pixelToCoord(event->pos().y());
        if (m_type == Type::XAxis)
        {
            XAxisLineUpdate(x_value);
            QString _pyCode = QString("PostProcessing.lineChartAxisLineUpdate('%1',%2, %3)").arg("x").arg(getLineChartStraightLineID()).arg(x_value);
            emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
        }
        else if (m_type == Type::YAxis)
        {
            YAxisLineUpdate(y_value);
            QString _pyCode = QString("PostProcessing.lineChartAxisLineUpdate('%1',%2, %3)").arg("y").arg(getLineChartStraightLineID()).arg(y_value);
            emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
        }
        parentPlot->replot();
    }

    void LineChartStraightLine::XLineLabelUpdate(double x_value)
    {
        auto parentPlot = qobject_cast<QCustomPlot*>(this->parent());
        if (parentPlot == nullptr)
        {
            return;
        }
        int index = 0;
        QString keyName;
        for (int i = 0; i < parentPlot->graphCount(); ++i)
        {
            ++index;
            auto graph = parentPlot->graph(i);
            const auto& graphData = graph->data();
            double value = std::numeric_limits<double>::quiet_NaN();
            const auto& iter = graphData->findBegin(x_value, false);
            // x点在线段范围中
            if (iter != graphData->constEnd())
            {
                // 原始数据点
                if (iter->key == x_value)
                {
                    value = iter->value;
                }
                // 非原始数据点，线性插值计算y值
                else
                {
                    bool hasData = false;
                    const auto& keyRange = graphData->keyRange(hasData);
                    if (hasData && (keyRange.contains(x_value)))
                    {
                        const auto& previousIter = graphData->findBegin(x_value);  // 距离最近的前一个原始数据点
                        const auto& nextIter = graphData->findEnd(x_value, false); // 距离最近的后一个原始数据点
                        double k = (nextIter->value - previousIter->value) / (nextIter->key - previousIter->key);
                        value = previousIter->value + k * (x_value - previousIter->key);
                    }
                }
            }
            if (std::isnan(value))
            {
                if (m_dataLabelList.size() > i)
                {
                    m_dataLabelList[i].first = nullptr;
                    m_dataLabelList.at(i).second.label->setVisible(false);
                    m_dataLabelList.at(i).second.arrow->setVisible(false);
                }
                continue;
            }
            if (i == m_dataLabelList.size())
            {
                IntersectionDataLabel intersectionDataLabel;
                auto position = this->createPosition(QString::number(this->positions().size()));
                intersectionDataLabel.label = new QCPItemText(parentPlot);
                intersectionDataLabel.label->setLayer("overlay");
                intersectionDataLabel.label->setSelectable(false);
                intersectionDataLabel.label->position->setParentAnchor(position);
                intersectionDataLabel.label->position->setCoords(35, 0);
                intersectionDataLabel.label->setPen(QPen(Qt::black));
                intersectionDataLabel.label->setBrush(Qt::white);
                intersectionDataLabel.label->setSelectedPen(QPen(Qt::blue));
                intersectionDataLabel.label->setSelectedBrush(Qt::white);
                intersectionDataLabel.label->setPadding({5, 5, 5, 5});
                intersectionDataLabel.arrow = new QCPItemLine(parentPlot);
                intersectionDataLabel.arrow->setLayer("overlay");
                intersectionDataLabel.arrow->setSelectable(false);
                intersectionDataLabel.arrow->start->setParentAnchor(intersectionDataLabel.label->left);
                intersectionDataLabel.arrow->end->setParentAnchor(position);
                intersectionDataLabel.arrow->setHead(QCPLineEnding::esLineArrow);
                m_dataLabelList.append(qMakePair(nullptr, intersectionDataLabel));
            }
            m_dataLabelList[i].first = graph;
            auto label = m_dataLabelList.at(i).second.label;
            dynamic_cast<QCPItemPosition*>(label->position->parentAnchor())->setCoords(x_value, value);
            label->setText(QString::number(value));
            label->setVisible(graph->visible());
            auto arrow = m_dataLabelList.at(i).second.arrow;
            arrow->setVisible(graph->visible());
        }
        for (int i = index; i < m_dataLabelList.size(); ++i)
        {
            m_dataLabelList[i].first = nullptr;
            m_dataLabelList.at(i).second.label->setVisible(false);
            m_dataLabelList.at(i).second.arrow->setVisible(false);
        }
        if (parentPlot->xAxis->range().contains(x_value))
        {
            m_axisLabel.label->position->setCoords(x_value, 1.03);
            m_axisLabel.label->setText(QString::number(x_value));
            m_axisLabel.label->setVisible(true);
            m_axisLabel.arrow->end->setCoords(x_value, 1);
            m_axisLabel.arrow->setVisible(true);
        }
        else
        {
            m_axisLabel.label->setVisible(false);
            m_axisLabel.arrow->setVisible(false);
        }
    }

    void LineChartStraightLine::YLineLabelUpdate(double y_value)
    {
        auto parentPlot = qobject_cast<QCustomPlot*>(this->parent());
        if (parentPlot == nullptr)
        {
            return;
        }
        int index = 0;
        for (int i = 0; i < parentPlot->graphCount(); ++i)
        {
            auto graph = parentPlot->graph(i);
            const auto& graphData = graph->data();
            QVector<QPair<QCPRange, QCPRange>> rangeArray;
            for (auto iter = graphData->begin(); iter != graphData->end(); ++iter)
            {
                const auto& previousIter = iter; // 前一个原始数据点
                const auto& nextIter = iter + 1; // 后一个原始数据点
                QCPRange keyRange, valueRange;
                if (nextIter != graphData->end())
                {
                    keyRange.lower = previousIter->key;
                    keyRange.upper = nextIter->key;
                    valueRange.lower = previousIter->value;
                    valueRange.upper = nextIter->value;
                }
                else
                {
                    keyRange.upper = keyRange.lower = previousIter->key;
                    valueRange.upper = valueRange.lower = previousIter->value;
                }
                bool isContains = false;
                if (valueRange.lower > valueRange.upper)
                {
                    if (y_value >= valueRange.upper && y_value <= valueRange.lower)
                    {
                        isContains = true;
                    }
                }
                else
                {
                    if (y_value >= valueRange.lower && y_value <= valueRange.upper)
                    {
                        isContains = true;
                    }
                }
                if (isContains)
                {
                    rangeArray.append(qMakePair(keyRange, valueRange));
                }
            }
            for (int j = 0; j < rangeArray.size(); ++j)
            {
                auto& range = rangeArray.at(j);
                double value = std::numeric_limits<double>::quiet_NaN();
                if (range.first.upper == range.first.lower)
                {
                    value = range.second.lower;
                }
                else
                {
                    double k = (range.second.upper - range.second.lower) / (range.first.upper - range.first.lower);
                    value = range.first.lower + (y_value - range.second.lower) / k;
                }
                if (std::isnan(value))
                {
                    m_dataLabelList[i].first = nullptr;
                    m_dataLabelList.at(i).second.label->setVisible(false);
                    m_dataLabelList.at(i).second.arrow->setVisible(false);
                    continue;
                }
                if (j + index == m_dataLabelList.size())
                {
                    IntersectionDataLabel intersectionDataLabel;
                    auto position = this->createPosition(QString::number(this->positions().size()));
                    intersectionDataLabel.label = new QCPItemText(parentPlot);
                    intersectionDataLabel.label->setLayer("overlay");
                    intersectionDataLabel.label->setSelectable(false);
                    intersectionDataLabel.label->position->setParentAnchor(position);
                    intersectionDataLabel.label->position->setCoords(0, -25);
                    intersectionDataLabel.label->setPen(QPen(Qt::black));
                    intersectionDataLabel.label->setBrush(Qt::white);
                    intersectionDataLabel.label->setSelectedPen(QPen(Qt::blue));
                    intersectionDataLabel.label->setSelectedBrush(Qt::white);
                    intersectionDataLabel.label->setPadding({5, 5, 5, 5});
                    intersectionDataLabel.arrow = new QCPItemLine(parentPlot);
                    intersectionDataLabel.arrow->setLayer("overlay");
                    intersectionDataLabel.arrow->setSelectable(false);
                    intersectionDataLabel.arrow->start->setParentAnchor(intersectionDataLabel.label->bottom);
                    intersectionDataLabel.arrow->end->setParentAnchor(position);
                    intersectionDataLabel.arrow->setHead(QCPLineEnding::esLineArrow);
                    m_dataLabelList.append(qMakePair(nullptr, intersectionDataLabel));
                }
                m_dataLabelList[j + index].first = graph;
                auto label = m_dataLabelList.at(j + index).second.label;
                dynamic_cast<QCPItemPosition*>(label->position->parentAnchor())->setCoords(value, y_value);
                label->setText(QString::number(value));
                label->setVisible(graph->visible());
                auto arrow = m_dataLabelList.at(j + index).second.arrow;
                arrow->setVisible(graph->visible());
            }
            index += rangeArray.size();
        }
        for (int j = index; j < m_dataLabelList.size(); ++j)
        {
            m_dataLabelList[j].first = nullptr;
            m_dataLabelList.at(j).second.label->setVisible(false);
            m_dataLabelList.at(j).second.arrow->setVisible(false);
        }
        if (parentPlot->yAxis->range().contains(y_value))
        {
            m_axisLabel.label->position->setCoords(-0.03, y_value);
            m_axisLabel.label->setText(QString::number(y_value));
            m_axisLabel.label->setVisible(true);
            m_axisLabel.arrow->end->setCoords(0, y_value);
            m_axisLabel.arrow->setVisible(true);
        }
        else
        {
            m_axisLabel.label->setVisible(false);
            m_axisLabel.arrow->setVisible(false);
        }
    }

    QDomElement& LineChartStraightLine::writeToProjectFile(QDomDocument* doc, QDomElement* element, bool isdiso)
    {
        QDomElement _LineChartStraightLineRoot = doc->createElement("LineChartStraightLine");
        QString _LineChartStraightLineType{""};
        if (m_type == XAxis)
        {
            _LineChartStraightLineType = "XAxis";
        }
        else if (m_type == YAxis)
        {
            _LineChartStraightLineType = "YAxis";
        }
        QDomAttr LineChartStraightLineTypeattr = doc->createAttribute("LineChartStraightLineType");
        LineChartStraightLineTypeattr.setValue(_LineChartStraightLineType);
        _LineChartStraightLineRoot.setAttributeNode(LineChartStraightLineTypeattr);

        GlobalHelperInstance->createDomElement(doc, &_LineChartStraightLineRoot, "StraightLineID", QString::number(m_straightLineID));
        GlobalHelperInstance->createDomElement(doc, &_LineChartStraightLineRoot, "XValue", QString::number(this->point1->coords().x()));
        GlobalHelperInstance->createDomElement(doc, &_LineChartStraightLineRoot, "YValue", QString::number(this->point1->coords().y()));

        element->appendChild(_LineChartStraightLineRoot);
        m_guideLineProperty->m_value = this->GetValue();
        m_guideLineProperty->writeToProjectFile(doc, &_LineChartStraightLineRoot, isdiso);

        return _LineChartStraightLineRoot;
    }

    void LineChartStraightLine::readFromProjectFile(QDomElement* element, bool isdiso)
    {
        auto _StraightLineIDNodeList = element->elementsByTagName("StraightLineID");
        m_straightLineID = _StraightLineIDNodeList.item(0).toElement().text().toInt();
        double _xValue = 0, _yValue = 0;
        auto _XValueNodeList = element->elementsByTagName("XValue");
        _xValue = _XValueNodeList.item(0).toElement().text().toDouble();
        auto _YValueNodeList = element->elementsByTagName("YValue");
        _yValue = _YValueNodeList.item(0).toElement().text().toDouble();
        if (m_type == XAxis)
        {
            XAxisLineUpdate(_xValue);
            QString _pyCode = QString("PostProcessing.lineChartAxisLineUpdate('%1',%2, %3)").arg("x").arg(getLineChartStraightLineID()).arg(_xValue);
            emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
        }
        else if (m_type == YAxis)
        {
            YAxisLineUpdate(_yValue);
            QString _pyCode = QString("PostProcessing.lineChartAxisLineUpdate('%1',%2, %3)").arg("y").arg(getLineChartStraightLineID()).arg(_yValue);
            emit IBaseSignalInstance->signal_execPYCode(QStringList{_pyCode}, true, false);
        }
        m_guideLineProperty->readFromProjectFile(&element->elementsByTagName("Style2D_GuideLineProperty").at(0).toElement(), isdiso);
    }
} // namespace pst
